let activeEffect = null;
const depsMap = new Map(); // 依赖收集 Map，key 为响应式对象的属性名，value 为依赖(environment函数)集合（Set）
// depsMap 格式：
// Map(3) {
//   'a' => Set(2) { [Function: environment], [Function: environment] },
//   'b' => Set(1) { [Function: environment] },
//   'c' => Set(1) { [Function: environment] }
// }

// 环境函数栈,解决嵌套effect函数的问题
const effectStack = [];

const track = (target, key) => {
  if (activeEffect) {
    let deps = depsMap.get(key); // 获取某个属性的依赖集合(一堆的environment函数)
    if (!deps) {
      // 如果该 key 还没有依赖集合，创建一个新的 Set（去重，避免重复收集同一个 effect）
      deps = new Set();
      depsMap.set(key, deps);
    }
    activeEffect.deps.push(deps); // 给当前这个环境函数
    deps.add(activeEffect); // 将当前激活的 环境函数加入依赖集合
    // 格式：activeEffect.deps = [Set(2) { [Function: environment], [Function: environment] }, Set(1) { [Function: environment] }]
  }
  console.log(depsMap);
};
const trigger = (target, key) => {
  const deps = depsMap.get(key); // 获取依赖集合
  if (deps) {
    // 如果存在依赖集合

    // 直接遍历 deps 出问题的核心，是 Set 遍历会 “实时响应集合的修改”—— 新增的元素会被加入遍历队列，已删除的元素会被跳过，最终导致新增的 env1 被反复遍历，形成无限循环
    const effectToRun = new Set(deps); // 复制一份依赖集合，生成某个瞬间的快照，避免在遍历过程中修改动态的依赖集合
    effectToRun.forEach((effect) => effect()); // 执行快照所有依赖环境函数，与其他环境函数互不干扰
  }
};

const data = {
  a: 1,
  b: 2,
  c: 3,
};
const state = new Proxy(data, {
  get(target, key) {
    track(target, key); // 进行依赖收集
    return target[key];
  },
  set(target, key, value) {
    target[key] = value;
    trigger(target, key); // 派发更新
    return true;
  },
});

function effect(fn) {
  // 每个effect函数维护一个环境函数
  // 形成闭包，将 fn 包裹在 environment 函数中
  const environment = () => {
    // 环境函数，用于激活和清空 effect
    activeEffect = environment; // 激活当前 effect
    effectStack.push(environment); // 入栈
    // 依赖清理
    clearDeps(environment);
    fn();
    activeEffect = effectStack.pop(); // 出栈
  };
  environment.deps = []; // 记录环境函数在哪些属性集合里
  environment();
}

/**
 * @param {*} environment 某个把的effect的回调函数包装好的环境函数，该环境函数可能被多个属性依赖，此时该环境函数已过期
 */
function clearDeps(environment) {
  // 拿到当前环境函数的依赖数组(可能有多个属性共同与该环境函数有关联)
  const deps = environment.deps;
  // 遍历依赖数组，将当前 effect 从每个依赖集合中删除
  if (deps.length) {
    deps.forEach((dep) => {
      // 从每个依赖集合中删除当前环境函数，其他effect生成的环境函数不干涉
      dep.delete(environment);
      if (dep.size === 0) {
        // 该属性的依赖集合为空，表示该属性正好只被该环境函数依赖，改属性的全局依赖一定也只有该环境函数，删除该属性的依赖集合
        for (let [key, value] of depsMap) {
          // key 为属性名，value 为依赖集合（一堆的environment函数）
          if (value === dep) {
            depsMap.delete(key); // 删除该属性的依赖集合
          }
        }
      }
    });
  }
  deps.length = 0; // 清空当前环境函数的依赖数组
}

effect(() => {
  effect(() => {
    effect(() => {
      // 嵌套函数执行完后面的响应式数据都收集不到
      state.c;
      console.log('执行了函数3');
    });
    state.a;
    console.log('执行了函数2');
  });
  state.b;
  console.log('执行了函数1');
});
